package com.yudean.esb.service.handler.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yudean.esb.cached.RecvPackageCollection;
import com.yudean.esb.common.Constants;
import com.yudean.esb.dao.esb.EsbRecvServiceConfigDao;
import com.yudean.esb.dao.esb.EsbRecvServicePackageDao;
import com.yudean.esb.dao.esb.EsbRecvServiceTempDataDao;
import com.yudean.esb.dao.esb.EsbRecvServiceTransactionDao;
import com.yudean.esb.domain.esb.EsbRecvServiceConfig;
import com.yudean.esb.domain.esb.EsbRecvServicePackage;
import com.yudean.esb.domain.esb.EsbRecvServiceTempData;
import com.yudean.esb.domain.esb.EsbRecvServiceTransaction;
import com.yudean.esb.service.handler.ReceiveDataService;
import com.yudean.esb.security.SecurtiyChecker;
import com.yudean.esb.security.SysAppUserService;
import com.yudean.esb.util.StringUtil;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据接受处理服务实现
 *
 * @author DL<duanlei#gdyd.com>
 */
@Service("receiveDataServiceImpl")
@Scope("prototype")
public class ReceiveDataServiceImpl implements ReceiveDataService {
    private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ReceiveDataServiceImpl.class);

    @Resource
    private SysAppUserService sysAppUserService;
    @Resource
    private SecurtiyChecker securtiyChecker;
    @Resource
    private EsbRecvServiceTempDataDao esbRecvServiceTempDataDao;
    @Resource
    private EsbRecvServiceTransactionDao esbRecvServiceTransactionDao;
    @Resource
    private EsbRecvServicePackageDao esbRecvServicePackageDao;
    @Resource
    private EsbRecvServiceConfigDao esbRecvServiceConfigDao;

    //根据数据处理结果返回信息
    private String result = "";
    private String msg = "";
    private String received = "";
    private String submitted = "";
    private String errorCode = "";
    private String errorMsg = "";

    private boolean validate(String serviceID, String token, String transactionID) {
        boolean isValid = true;
        // 验证token
        String[] userInfo = securtiyChecker.getUserInfo(token);
        if (sysAppUserService != null) {
            if (userInfo == null || !sysAppUserService.hasPermit(userInfo[0], serviceID)) {
                errorCode = Constants.CODE_AUTH_ERROR;
                errorMsg = "没有访问的权限";
                isValid = false;
            }
        }
        //检查serviceID
        if (StringUtil.isEmpty(serviceID)) {
            errorCode = Constants.CODE_CLIENT_ERROR;
            errorMsg = "serviceID不能为空";
            isValid = false;
        } else {
            EsbRecvServiceConfig serviceConfig = esbRecvServiceConfigDao.getByServiceID(serviceID);
            if (serviceConfig == null) {
                errorCode = Constants.CODE_CLIENT_ERROR;
                errorMsg = "非法serviceID";
                isValid = false;
            }
        }
        // 验证事务ID
        if (StringUtil.isEmpty(transactionID)) {
            errorCode = Constants.CODE_CLIENT_ERROR;
            errorMsg = "事务ID不能为空";
            isValid = false;
        } else {
            boolean isSecurity = securtiyChecker.verifyID(transactionID);
            if (!isSecurity) {
                errorCode = Constants.CODE_CLIENT_ERROR;
                errorMsg = "非法事务ID";
                isValid = false;
            }
        }
        return isValid;
    }

    @Override
    public String put(String serviceID, String token, String transactionID,
                      String pkg, String totalPackage, String rollback, String forceReplace, String data) {

        LOGGER.debug("put():request-{serviceID:{},token:{},transationID:{},"
                        + "package:{},totalPackage:{},rollback:{},forceReplace:{},data:{}}",
                serviceID, token, transactionID, pkg, totalPackage, rollback, forceReplace, data);

        if (!validate(serviceID, token, transactionID)) {
            return errorMsg(serviceID, token, transactionID, errorCode, errorMsg);
        }

        // 检查是否Rollback
        if (rollback != null && rollback.equals("1")) {
            EsbRecvServiceTempData tempData = new EsbRecvServiceTempData();
            tempData.setTransactionID(transactionID);
            esbRecvServiceTempDataDao.delete(tempData);
            EsbRecvServicePackage pack = new EsbRecvServicePackage();
            pack.setTransactionID(transactionID);
            esbRecvServicePackageDao.delete(pack);
            EsbRecvServiceTransaction transaction = new EsbRecvServiceTransaction();
            transaction.setTransactionID(transactionID);
            esbRecvServiceTransactionDao.delete(transaction);

            result = Constants.CODE_SUCCESS;
            msg = Constants.CODE_SUCCESS_MSG;
            LOGGER.info("rollback data:serviceID={},transactionID={} submitted data number={}", serviceID, transactionID, submitted);
            return wrap2XML(serviceID, token, transactionID, result, msg, received, submitted);
        }

        if (data == null || "".equals(data.trim())) {
            return noData(serviceID, token, transactionID);
        }
        try {
            data = java.net.URLDecoder.decode(data, "utf-8");
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("data URLDecode error......");
            e.printStackTrace();
        }
        //替换出现的反斜杠"\"为正斜杠"/"
        if (data.indexOf("\\") > 0) {
            data = data.replaceAll("\\\\", "/");
        }

        //保存结果集
        ArrayList<Map<String, Object>> requestList = null;
        try {
            JSONArray list = JSONArray.parseArray(data);
            //如果请求数据内容合法的话，开始进行数据转换
            requestList = new ArrayList<Map<String, Object>>();
            for (int idx = 0, size = list.size(); idx < size; idx++) {
                //将数据转换成Map
                Map<String, Object> item = new HashMap<String, Object>();
                JSONObject obj = list.getJSONObject(idx);
                for (Map.Entry<String, Object> entry : obj.entrySet()) {
                    String key = entry.getKey();
                    Object val = entry.getValue();
                    item.put(key, val);
                }
                requestList.add(item);
            }
        } catch (Exception e) {
            LOGGER.error("put():e-{}", e.getMessage());
            e.printStackTrace();
            return errorMsg(serviceID, token, transactionID, Constants.CODE_CLIENT_ERROR, "发送数据解析错误！");
        }

        //进行数据处理
        boolean isSuccess = processReceivedData(serviceID, transactionID, pkg, totalPackage, rollback, forceReplace, requestList);

        if (isSuccess) {
            LOGGER.debug("put():process data success");
            result = Constants.CODE_SUCCESS;
            msg = Constants.CODE_SUCCESS_MSG;
            received = String.valueOf(requestList.size());
            //当接收到的总包数等于事务包数，返回事务成功提交的总记录数
            if (RecvPackageCollection.isTransactionEnd(transactionID)) {
                submitted = String.valueOf(esbRecvServiceTempDataDao.count(transactionID));
                LOGGER.debug("put():serviceID={},transactionID={} submitted data number={}", serviceID, transactionID, submitted);
            }
        } else {
            LOGGER.debug("put():process data fail");
            result = Constants.CODE_ERROR;
            received = String.valueOf(requestList.size());
        }
        return wrap2XML(serviceID, token, transactionID, result, msg, received,
                submitted);
    }

    @Override
    public String put(String serviceID, String token, String transactionID,
                      String pkg, String totalPackage, String rollback, String data) {
        return this.put(serviceID, token, transactionID, pkg, totalPackage, rollback, "0", data);
    }

    /**
     * 接收数据处理
     *
     * @param serviceID
     * @param transactionID
     * @param pkg
     * @param totalPackage
     * @param rollback
     * @param requestList
     */
    public boolean processReceivedData(String serviceID, String transactionID,
                                       String pkg, String totalPackage, String rollback, ArrayList<Map<String, Object>> requestList) {
        boolean transactionDaoReuslt = false;
        boolean packageDaoReuslt = false;
        boolean tempDataDaoReuslt = false;
        boolean isPkgAddSuccess = false;
        boolean isSuccess = false;

        //缓存事务当前接收的数据包序号
        isPkgAddSuccess = RecvPackageCollection.addPackage(transactionID, pkg, totalPackage);
        if (!isPkgAddSuccess) {
            msg = "packageId " + pkg + " repeat";
            return false;
        }

        //写事务记录表
        if (RecvPackageCollection.isTransactionBegin(transactionID)) {
            transactionDaoReuslt = esbRecvServiceTransactionDao.insert(serviceID, transactionID, totalPackage, rollback);
        }

        //记录事务ID下的数据包
        packageDaoReuslt = esbRecvServicePackageDao.insert(transactionID, pkg);

        //写临时数据表
        tempDataDaoReuslt = esbRecvServiceTempDataDao.batchInsert(serviceID, transactionID, pkg, requestList);

        //插入表数据成功后设置返回标志
        if (RecvPackageCollection.isTransactionBegin(transactionID)) {
            isSuccess = transactionDaoReuslt && packageDaoReuslt && tempDataDaoReuslt;
        } else {
            isSuccess = packageDaoReuslt && tempDataDaoReuslt;
        }
        //事务结束后更新事务表结果标志位
        if (isSuccess && RecvPackageCollection.isTransactionEnd(transactionID)) {
            EsbRecvServiceTransaction transaction = new EsbRecvServiceTransaction();
            transaction.setTransactionID(transactionID);
            transaction.setResult("S");
            esbRecvServiceTransactionDao.update(transaction);
        }
        if (!isSuccess) {
            msg = Constants.CODE_DB_ERROR_MSG;
        }
        return isSuccess;
    }

    /**
     * 接收数据处理
     *
     * @param serviceID
     * @param transactionID
     * @param pkg
     * @param totalPackage
     * @param rollback
     * @param forceReplace
     * @param requestList
     */
    public boolean processReceivedData(String serviceID, String transactionID,
                                       String pkg, String totalPackage, String rollback, String forceReplace,
                                       ArrayList<Map<String, Object>> requestList) {
        boolean transactionDaoReuslt = false;
        boolean packageDaoReuslt = false;
        boolean tempDataDaoReuslt = false;
        boolean isPkgAddSuccess = false;
        boolean isSuccess = false;

        //forceReplace默认为"0"
        if (StringUtil.isEmpty(forceReplace)) {
            forceReplace = "0";
        }

        //缓存事务当前接收的数据包序号
        isPkgAddSuccess = RecvPackageCollection.addPackage(transactionID, pkg, totalPackage, forceReplace);
        if (!isPkgAddSuccess) {
            msg = "PackageId " + pkg + " repeat";
            return false;
        }

        //写事务记录表
        if (RecvPackageCollection.isTransactionBegin(transactionID)) {
            transactionDaoReuslt = esbRecvServiceTransactionDao.insert(serviceID, transactionID, totalPackage, rollback);
        }

        //记录事务ID下的数据包
        packageDaoReuslt = esbRecvServicePackageDao.insert(transactionID, pkg, forceReplace);

        //写临时数据表
        tempDataDaoReuslt = esbRecvServiceTempDataDao.batchInsert(serviceID, transactionID, pkg, forceReplace, requestList);

        //插入表数据成功后设置返回标志
        if (RecvPackageCollection.isTransactionBegin(transactionID)) {
            isSuccess = transactionDaoReuslt && packageDaoReuslt && tempDataDaoReuslt;
        } else {
            isSuccess = packageDaoReuslt && tempDataDaoReuslt;
        }
        //事务结束后更新事务表结果标志位
        if (isSuccess && RecvPackageCollection.isTransactionEnd(transactionID)) {
            EsbRecvServiceTransaction transaction = new EsbRecvServiceTransaction();
            transaction.setTransactionID(transactionID);
            transaction.setResult("S");
            esbRecvServiceTransactionDao.update(transaction);
        }
        if (!isSuccess) {
            msg = Constants.CODE_DB_ERROR_MSG;
        }
        return isSuccess;
    }

    /**
     * 将结果封装成XML数据
     *
     * @param serviceID
     * @param token
     * @param transationID
     * @param result
     * @param msg
     * @param received
     * @param submitted
     * @return
     */
    private String wrap2XML(String serviceID, String token,
                            String transationID, String result, String msg, String received,
                            String submitted) {
        Document doc = DocumentHelper.createDocument();
        Element serviceElem = doc.addElement("service");
        try {
            Element idElem = serviceElem.addElement("serviceID");
            idElem.addText(serviceID);

            Element tokenElem = serviceElem.addElement("token");
            tokenElem.addText(token);

            Element transtationElem = serviceElem.addElement("transactionID");
            transtationElem.addText(transationID);

            Element resultElem = serviceElem.addElement("result");
            resultElem.addText(result);

            Element msgElem = serviceElem.addElement("msg");
            msgElem.addText(msg);

            Element receivedElem = serviceElem.addElement("received");
            receivedElem.addText(received);

            Element submittedElem = serviceElem.addElement("submitted");
            submittedElem.addText(submitted);

        } catch (Exception e) {
            LOGGER.error("wrap2XML():e-{}", e.getMessage());
            return errorMsg(serviceID, token, transationID, Constants.CODE_SERVER_ERROR, "生成XML数据内容发生异常！");
        }
        return doc.asXML();
    }

    /**
     * 返回客户端无数据内容
     *
     * @return
     */
    private static String noData(String serviceID, String token, String transactionID) {
        return errorMsg(serviceID, token, transactionID, Constants.CODE_CLIENT_ERROR, "发送无数据内容!");
    }

    /**
     * 返回错误信息
     *
     * @param serviceID
     * @param token
     * @param transactionID
     * @param result        错误结果
     * @param msg           错误信息详情
     * @return
     */
    private static String errorMsg(String serviceID, String token,
                                   String transactionID, String result, String msg) {
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><service>")
                .append("<serviceID>").append(serviceID).append("</serviceID>")
                .append("<token>").append(token).append("</token>")
                .append("<transactionID>").append(transactionID)
                .append("</transactionID>").append("<result>").append(result)
                .append("</result><msg>").append(msg)
                .append("</msg></service>");
        return sb.toString();
    }
}
